在上一节中我们简单介绍了进程的概念,还有父进程和子进程
这篇文章的主要内容是介绍如何使用fork函数创建子进程,fork函数的一些特点
查看进程的另一种方式在上一节中主要使用ps命令列举进程条目,但我们知道,在Linux系统中,一切皆文件
因此所谓的进程条目,本质上也是存着的一个动态文件proc,这里面存放着所有的进程信息
因为这个文件是会随着进程的改变实时更新其中的内容,因此我们称之为动态文件
我们可以使用ls /proc/查看所有进程文件,也可以在其后加上特定进程的pid,查看特定进程
例如
我们同样可以创建一个死循环,然后来使用这个命令来查看其中的内容
在我们自行创建的进程当中存在着许多文件,这里有两个文件值得我们注意,一个是exe文件,另一个是cwd文件
其中exe文件指的是可执行程序的位置,而cwd代表默认的当前文件,或者可以简单理解为当前的文件路径,就好比pwd命令查看当前路径,他就是从cwd文件获取的路径
如何创建子进程众所周知Linux的底层是使用c语言实现的,因此所谓的创建进程本质上就是调用了一个c语言函数,也就是用代码创建进程,而我们用户使用代码创建进程称...
我们在上一节内容中有简单谈到进程的感性理解,他在课本上的概念是,程序的一个执行实例或正在执行的程序
但在本质上,他其实就是一个被分配了系统资源(CPU,内存)的实体
PCB上一节内容中,我们说到,管理是管理的数据,需要先描述再组织
那在内存中,操作系统对多个进程应当如何管理呢
这时候就有聪明的同学回答了,先描述再组织
我们还是使用结构体来进行描述,对于一个进程来说可能会有进程ID、代码存储地址、进程状态、进程优先级,如果是用链表的形式存储还会有指向下一个结构体的指针
12345678struct data{ // id // 代码地址 // 状态 // 优先级 struct data* next;};
那么这样一个结构体,我们给她起一个名字,叫做PCB,进程控制块
当然在不同的操作系统中,管理所用的PCB名称也不一定相同,在Linux中,这个PCB具体叫做struct task_struct,进程控制块
那么操作系统对进程的管理就变成了对链表的增删查改
进程排队当进程在链表中,就会等待CPU去找PCB来进行处理,那么CPU是如何知...
在深入学习Linux进程等概念之前,我们对于计算机的体系结构还需要有一个基础的认识,这一部分在大多高校中复杂冗长,但十分严谨广泛适用
我们在这里主要介绍的是感性的直观上对计算机工作原理系统调度的理解
冯诺依曼体系结构现代计算机都是基于冯诺依曼体系结构的,也就是我们在计算机组成原理上学的那个东西
当然现代计算机不以存储器为核心,而以运算器和控制器为核心,这些都是后话了,我们也不深入了解其中的原理,只分别介绍一下这些在计算机中代表什么
输入设备主要有,键盘、鼠标、摄像头、磁盘、网卡等
存储器,这里的存储器指的是内存而非磁盘
中央处理器有,运算器和和控制器,其中也有一部分cache缓存
输出设备主要有、显示器、磁盘、扬声器、网卡等
内存体系用一张图就可以解释清楚
由下到上是计算机的内存分级,因为不同存储器的处理数据速度不尽相同,而处理的速度越快,就导致成本越高,在总成本有限的情况下,使用这样的金字塔体系就可以保证有相当快的数据存取速度和相当大的存储空间了
而整个计算机中最快的就是CPU了,为了不被磁盘拖后腿,我们只允许CPU和内存(主存)以上的存储器打交道,这样就能在保证速度的同时...
智能指针前面我们一直在铺垫智能指针,这个词听上去非常高大上,但其实我们不需要害怕,就像一开始我们学什么封装继承多态,这些东西本质上就是对类和对象的补充
智能指针出现的背景其实为了是内存空间管理的问题,尤其是加了异常处理机制之后,就变得更为复杂
那我们其实就想,有没有一种东西,可以自动的控制内存资源
RAII思想RAII思想说白了就是一种利用对象的声明周期来控制程序资源的简单技术,例如内存、文件句柄、网络连接等
在对象构造的时候,获取资源,在对象析构的时候,释放资源,让这个对象在生命周期之内,始终可以进行访问
这样做的本质其实是将这个资源托管给了对象,我们之前学类和对象的时候,他是可以自动调用构造和析构函数的,这就意味着我们不需要显示释放资源,并且可以让资源在其生命周期内始终有效,因为在调用析构的时候,才会释放资源
那更直白一点,所谓的智能指针本质上其实就是一个类,这个类可以管理资源
例如
12345678910111213141516template<class T>class SmartPtr{public: SmartPtr(T* ptr = nu...
logistic回归logistic regression被称之为logistic回归,对于logistic这个单词来说,他本身的翻译其实不太容易,比较有名的译法是对数几率回归,我也认为这种译法是比较合适的,虽然并非logistic的本意,但却是最贴切这个算法本身的译法
regression的意思是回归,但其实这个算法是一种分类算法
回到我们讲线性回归的时候,主要是对指数本身进行预测,但是想要更实用的话,我们其实更希望知道涨跌的情况,那其实这样的输出值就只有两类,涨或者跌,也就是二分类问题
这种情况其实非常常见,例如销售额和顾客买不买之间的问题,播放量和用户会不会点击的问题
但问题在于,我们统计出来的样本特征往往是一个连续的实数值,而目标则是一个0或1的问题,那么我们简单的线性回归模型就会失效,难以再进行有效的预测
如果直接拟合比较困难,那我们可以将输出值变化一下,从一个非0即1的问题变成涨跌的概率问题,那么概率是连续值,我们就又回到了从连续值到连续值的映射,这似乎就还是回归的内容
那我们给出一个阈值($\theta$),当概率大于阈值时,认为涨的可能性大,当概率小于阈值时,认为...
异常在我们之前学习的时候,C语言遇到内存泄漏或者内存越界的时候,要么程序直接就崩了,给一个错误码,写的好一点也就是给个assert,但是程序仍会终止,要么就根本发现不来,C++中我们也常说,当越界时会抛异常
异常的概念异常是是C++处理程序错误的一种方式,当程序发生错误的时候,就会抛出异常,这个异常可以是程序自发给出的,也有可能是我们手动写的throw,当抛出异常之后,我们可以在别的地方捕获异常,catch到就可以对异常进行提示和处理
这样做有一个好处是,当程序出错时不一定会完全终止程序,而是可以有一个补救措施
一般有三个关键字
throw:这个就是扔出去的意思,扔出一个异常,所谓的异常其实就是一个类
catch:这个是捕获异常,当捕获到了这个异常(类)的时候,需要执行的语句
try:这个是用于执行可能会出错的代码,在代码中出错的语句抛出就可以被catch到
例如
123456789101112131415161718double func(int a, int b){ if(b==0) throw "division by zero&qu...
类的新增功能默认构造在原有的C++类中,会有6个默认的成员函数,分别是构造函数、析构函数、拷贝构造、赋值重载、取地址重载、const取地址重载
最重要并且经常用的是前四个,后两个用处不大
所谓默认成员函数是当构建一个类的时候,我们不写这些函数,编译器会帮我们生成一个默认的
在C++11中,又新增了移动构造函数和移动赋值重载,这两个我们在上一篇中有详细解释
但是生成的规则会有不同
当你没有显式的写析构、拷贝构造和赋值重载时,编译器会自动生成默认的移动构造和移动赋值重载
默认生成的移动构造函数,对于内置类型会执行按字节拷贝,对于自定义类型,就需要看这个类型是否定义移动构造,有的话就直接调用,没有的话就调用拷贝构造
移动赋值重载和上面的移动构造完全类似
但是当我们自己写了移动构造或者移动赋值,编译器就不会生成拷贝构造和赋值重载了
类成员变量初始化C++11允许在类定义的时候给成员变量缺省值,默认生成的构造函数会使用这些缺省值进行构造,例如
1234567class Date{private: int _year = 1970; int _month = 1; in...
右值引用和移动语义左值引用和右值引用所谓的引用就是给变量起别名,那么左值引用和右值引用的区别其实就在于左值和右值
左值与左值引用左值表示的是一个数据的表达式,比如说变量的名字,或者是解引用的指针,我们可以获取左值的地址,并且可以对左值赋值,左值可以出现在赋值符号的左边,而右值不行
const的左值不能赋值但是可以取地址,左值引用就是给左值取引用,左值引用依然是左值
例如
1234567891011121314int main(){ // 左值 int a = 1; int* pa = &a; const int b = 2; // 左值引用 int& ra = a; int*& rpa = pa; const int& b = b; int& rpaval = *pa; return 0;}
这些用法是我们可以容易理解的
右值与右值引用右值的本质上也是数据的表达式,但是与左值有不同,例如字面常量,表达式的返回值,函数的返回值(非左值引用返回),右值可以...